package com.hsxxherp.simplify.framework.algorithm;


import com.hsxxherp.simplify.framework.exception.BusinessException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.util.Base64;

/**
 * SM2Util 工具类
 *
 * @author Mark·虎克 (2486970363@qq.com)
 */
public class Sm2Util {

    private static final String ALGORITHM = "SM2";
    private static final String PROVIDER = "BC";

    private static final ECParameterSpec EC_PARAMETER_SPEC = ECNamedCurveTable.getParameterSpec("sm2p256v1");

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 生成 SM2 密钥对
     *
     * @return 密钥对
     */
    public static KeyPair generateKeyPair() {
        try {
            KeyPairGenerator keyPairGenerator;
            keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM, PROVIDER);
            keyPairGenerator.initialize(EC_PARAMETER_SPEC, new SecureRandom());
            return keyPairGenerator.generateKeyPair();
        } catch (Exception e) {
            throw new RuntimeException("SM2 密钥对生成失败！", e);
        }

    }

    /**
     * SM2 加密
     *
     * @param publicKey 公钥
     * @param data      待加密数据
     * @return 加密后的数据（Base64 编码）
     */
    public static String encrypt(PublicKey publicKey, byte[] data) {
        try {
            BCECPublicKey bcecPublicKey = (BCECPublicKey) publicKey;
            ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(bcecPublicKey.getQ(), new ECDomainParameters(
                    EC_PARAMETER_SPEC.getCurve(),
                    EC_PARAMETER_SPEC.getG(),
                    EC_PARAMETER_SPEC.getN()
            ));
            SM2Engine sm2Engine = new SM2Engine();
            sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
            byte[] encryptedBytes = sm2Engine.processBlock(data, 0, data.length);
            return Base64.getEncoder().encodeToString(encryptedBytes);
        } catch (InvalidCipherTextException e) {
            throw new BusinessException("SM2 加密失败！无效密钥。", e);
        }
    }

    /**
     * SM2 解密
     *
     * @param privateKey    私钥
     * @param encryptedData 加密数据（Base64 编码）
     * @return 解密后的数据
     */
    public static byte[] decrypt(PrivateKey privateKey, String encryptedData) throws InvalidCipherTextException {
        try {
            BCECPrivateKey bcecPrivateKey = (BCECPrivateKey) privateKey;
            ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(bcecPrivateKey.getD(), new ECDomainParameters(
                    EC_PARAMETER_SPEC.getCurve(),
                    EC_PARAMETER_SPEC.getG(),
                    EC_PARAMETER_SPEC.getN()
            ));
            SM2Engine sm2Engine = new SM2Engine();
            sm2Engine.init(false, privateKeyParameters);
            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
            return sm2Engine.processBlock(encryptedBytes, 0, encryptedBytes.length);
        } catch (InvalidCipherTextException e) {
            throw new RuntimeException("SM2 解密失败！无效密钥。", e);
        }
    }

    /**
     * SM2 签名
     *
     * @param privateKey 私钥
     * @param data       待签名数据
     * @return 签名结果（Base64 编码）
     */
    public static String sign(PrivateKey privateKey, byte[] data) {
        try {
            BCECPrivateKey bcecPrivateKey = (BCECPrivateKey) privateKey;
            Signature signature = Signature.getInstance("SM3withSM2", PROVIDER);
            signature.initSign(privateKey);
            signature.update(data);
            byte[] signBytes = signature.sign();
            return Base64.getEncoder().encodeToString(signBytes);
        } catch (Exception e) {
            throw new RuntimeException("SM2 签名失败！", e);
        }
    }

    /**
     * SM2 验签
     *
     * @param publicKey 公钥
     * @param data      原始数据
     * @param sign      签名结果（Base64 编码）
     * @return 验签结果
     */
    public static boolean verify(PublicKey publicKey, byte[] data, String sign) {
        try {
            Signature signature = Signature.getInstance("SM3withSM2", PROVIDER);
            signature.initVerify(publicKey);
            signature.update(data);
            byte[] signBytes = Base64.getDecoder().decode(sign);
            return signature.verify(signBytes);
        } catch (Exception e) {
            throw new RuntimeException("SM2 验签失败！", e);
        }
    }

    /**
     * 将十六进制公钥字符串转换为 PublicKey 对象
     *
     * @param publicKeyHex 十六进制公钥字符串
     * @return PublicKey 对象
     */
    public static PublicKey getPublicKeyFromHex(String publicKeyHex) {
        try {
            byte[] publicKeyBytes = Hex.decode(publicKeyHex);
            ECPoint ecPoint = EC_PARAMETER_SPEC.getCurve().decodePoint(publicKeyBytes);
            ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(ecPoint, EC_PARAMETER_SPEC);
            KeyFactory keyFactory;
            keyFactory = KeyFactory.getInstance(ALGORITHM, PROVIDER);
            return keyFactory.generatePublic(publicKeySpec);
        } catch (Exception e) {
            throw new RuntimeException("SM2 公钥转换失败！", e);
        }
    }

    /**
     * 将十六进制私钥字符串转换为 PrivateKey 对象
     *
     * @param privateKeyHex 十六进制私钥字符串
     * @return PrivateKey 对象
     */
    public static PrivateKey getPrivateKeyFromHex(String privateKeyHex) {
        try {
            BigInteger privateKeyValue = new BigInteger(privateKeyHex, 16);
            ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(privateKeyValue, EC_PARAMETER_SPEC);
            KeyFactory keyFactory;
            keyFactory = KeyFactory.getInstance(ALGORITHM, PROVIDER);
            return keyFactory.generatePrivate(privateKeySpec);
        } catch (Exception e) {
            throw new RuntimeException("SM2 私钥转换失败！", e);
        }
    }
}